import is.glif.fenius.ws.draft.DraftFeniusService;
import is.glif.fenius.ws.draft.Draft;

import is.glif.fenius.ws.draft.ReservationParams;
import is.glif.fenius.ws.draft.EndpointPair;
import is.glif.fenius.ws.draft.ExplicitRoute;
import is.glif.fenius.ws.draft.EndpointParams;
import is.glif.fenius.ws.draft.CommonParams;
import is.glif.fenius.ws.draft.DomainParams;
import is.glif.fenius.ws.draft.Status;
import is.glif.fenius.ws.draft.TimeParams;
import is.glif.fenius.ws.draft.AvpSeq;
import is.glif.fenius.ws.draft.Avp;
import is.glif.fenius.ws.draft.Direction;
import is.glif.fenius.ws.draft.Reliability;

import is.glif.fenius.ws.draft.CreateReq;
import is.glif.fenius.ws.draft.CreateResp;
import is.glif.fenius.ws.draft.ListReq;
import is.glif.fenius.ws.draft.ListResp;
import is.glif.fenius.ws.draft.CancelReq;
import is.glif.fenius.ws.draft.CancelResp;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

import java.util.List;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.GregorianCalendar;

import java.net.URL;

public class DraftTest {
    static boolean debug = false;
    // public static String apoint = "IXA1";
    // public static String zpoint = "IXB2";
    public static String apoint = "g-lambda.net:AKIBA";
    public static String zpoint = "g-lambda.net:TSUKUBA";
    // public static String apoint = "startap.net:e1200:te1/0:*";
    // public static String zpoint = "startap.net:e1200:te1/1:*";
    // public static String vlanid = "100";
    public static String vlanid = "0";
    // public static int bw = 1000 * 1;
    public static int bw = 10 * 1;


    private DraftFeniusService tl = null;

    public DraftTest(DraftFeniusService tl) 
    {
	this.tl = tl;
    }

    static boolean isUseSSL = true;
    static boolean isUseBasicHttpAuth = true;

    static String timezone = "UTC";
    static QName qname = new QName("http://fenius.glif.is/ws/draft/", "draft");
    // String surl = "http://jupiter.es.net:8080/DraftFeniusService?wsdl";
    // String surl = "http://150.183.236.50:8081/DraftFeniusService?wsdl";
    // String surl = "http://amy.phosphorus.i2cat.cat:8080/DraftFeniusService?wsdl";
    static String purl = "http://163.220.30.149:8080/DraftFeniusService?wsdl";
    static String surl = "https://163.220.30.149:8080/DraftFeniusService?wsdl";

    // static String sslTruststorePath = "config/client-truststore";
    // static String sslTruststorePass = "clientkey";
    // static String sslKeystorePath = "config/client-keystore";
    // static String sslKeystorePass = "clientkey";
    static String sslTruststorePath = "../config/myserver-t";
    static String sslTruststorePass = "serverkey";
    static String sslKeystorePath = "../config/myclient";
    static String sslKeystorePass = "clientkey";
    static String httpUserName = "username";
    static String httpUserPass = "password";


    static private DraftFeniusService initSoap(String wsdlLocation, QName serviceName)
    {
        if (isUseSSL) {
            System.setProperty("javax.net.ssl.trustStoreType", "jks");
            System.setProperty("javax.net.ssl.trustStore", sslTruststorePath);
            System.setProperty("javax.net.ssl.trustStorePassword", sslTruststorePass);
              
            System.setProperty("javax.net.ssl.keyStoreType", "jks");
            System.setProperty("javax.net.ssl.keyStore", sslKeystorePath);
            System.setProperty("javax.net.ssl.keyStorePassword", sslKeystorePass);
        }
        
        URL wsdlURL = null;

        try {
            wsdlURL = new URL(wsdlLocation);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
        
        Draft ss = new Draft(wsdlURL, serviceName);
        DraftFeniusService port = ss.getDraftFeniusServicePort();
        
        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(port); 
        HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
        HTTPClientPolicy cp = new HTTPClientPolicy();
        Integer timeout = new Integer(10*1000);
        Long connTO = new Long(timeout);
        Long rcvTO = new Long(timeout);
        
        cp.setConnectionTimeout(connTO);
        cp.setReceiveTimeout(rcvTO);
        httpConduit.setClient(cp);

        if (isUseSSL) {
	    /*
	    org.apache.cxf.endpoint.Client client2 = ClientProxy.getClient(port);
	    HTTPConduit httpConduit2 = (HTTPConduit) client2.getConduit();
 	    TLSClientParameters tlsParams = new TLSClientParameters(); 
	    tlsParams.setDisableCNCheck(true); 

	    FiltersType filter = new FiltersType(); 
	    filter.getInclude().add(".*_EXPORT_.*"); 
	    filter.getInclude().add(".*_EXPORT1024_.*"); 
	    filter.getInclude().add(".*_WITH_DES_.*"); 
	    filter.getInclude().add(".*_WITH_NULL_.*"); 
	    filter.getExclude().add(".*_DH_anon_.*"); 
	    tlsParams.setCipherSuitesFilter(filter); 
        
	    httpConduit2.setTlsClientParameters(tlsParams); 
	    */
 	    TLSClientParameters tlsParams = new TLSClientParameters(); 
	    tlsParams.setDisableCNCheck(true); 

	    FiltersType filter = new FiltersType(); 
	    filter.getInclude().add(".*_EXPORT_.*"); 
	    filter.getInclude().add(".*_EXPORT1024_.*"); 
	    filter.getInclude().add(".*_WITH_DES_.*"); 
	    filter.getInclude().add(".*_WITH_NULL_.*"); 
	    filter.getExclude().add(".*_DH_anon_.*"); 
	    tlsParams.setCipherSuitesFilter(filter); 
        
	    httpConduit.setTlsClientParameters(tlsParams); 
        }

        if (isUseBasicHttpAuth) {
            BindingProvider bp = (BindingProvider) port;
            bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, 
				       httpUserName);
            bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, 
				       httpUserPass);
        }

	return port;
    }
	
    public boolean create(ReservationParams p) 
    {
	if (tl == null) return false;

	try {
	    if (debug) printParams("Create request", p);
	    CreateReq req = new CreateReq();
	    req.setResvParams(p);

	    // Thread.sleep(10*1000);
	    long T_create_s = System.nanoTime();
	    CreateResp res = tl.create(req);
	    long T_create_e = System.nanoTime();
	    // Thread.sleep(10*1000);
	    System.err.println("\t" + (T_create_e - T_create_s)/1000);

	    ReservationParams rp = res.getResvParams();
	    if (debug) printParams("Create response", rp);

	    if (true) {
		cancel(rp.getReservationId());
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	    return false;
	}
	return true;
    }

    public boolean list() 
    {
	if (tl == null) return false;

	try {
	    if (debug) printParams("List request", null);
	    ListReq req = new ListReq();

	    ListResp res = tl.list(req);

	    List<ReservationParams> rps = res.getReservations();
	    if (rps == null) return false;

	    int size = rps.size();
	    ReservationParams[] rp = new ReservationParams[size];
	    ReservationParams[] rpp = rps.toArray(rp);
	    for (int i = 0; i < size; i++) {
		if (debug) printParams("List response", rpp[i]);
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	    return false;
	}
	return true;
    }

    public boolean cancel(String id)
    {
	if (tl == null) return false;

	try {
	    if (debug) printParams("Cancel request", null);

	    CancelReq req = new CancelReq();
	    req.setReservationId(id);
	    req.setUserName("fenius-user");
	    req.setUserGroup("fenius-group");

	    CancelResp res = tl.cancel(req);

	    Status status = res.getStatus();
	    if (debug) System.err.println("Cancel result = " + status.toString());
	} catch (Exception e) {
	    e.printStackTrace();
	    return false;
	}
	return true;
    }

    private void printParams(String m, ReservationParams p)
    {
	String s = m + ":\n";
	if (p != null) {
	    m += "\t" + p.getReservationId() 
		+ "::" + p.getEndpoints().getA()
		+ "->" + p.getEndpoints().getZ()
		+ ":" + p.getEndtoendParams().getStatus().toString();

	    XMLGregorianCalendar c = 
		p.getEndtoendParams().getTimeParams().getScheduledStartTime();
	    m += ", From " + c.getYear() + "/" + c.getMonth() + "/" + c.getDay();
	    m += " " + c.getHour() + ":" + c.getMinute() + ":" + c.getSecond();

	    c = p.getEndtoendParams().getTimeParams().getScheduledEndTime();
	    m += ", To " /* + c.getYear() + "/" + c.getMonth() + "/" + c.getDay() */;
	    m += " " + c.getHour() + ":" + c.getMinute() + ":" + c.getSecond();

	    m += ", BW=" + p.getEndtoendParams().getBandwidth();
	    m += ", VLAN=" + p.getEndtoendParams().getAdhocParams().getAvp().get(0).getAttrVal();
	}
	if (debug) System.out.println(m);
    }

    public boolean createTest() 
    {
	int duration = 10; /* minute */
	int waitstart = 10; /* minute */

	Calendar startTime = Calendar.getInstance();
	startTime.setTime(new Date());
	startTime.set(Calendar.MILLISECOND, 0);
	startTime.add(Calendar.MINUTE, waitstart);
	Calendar endTime = (Calendar) startTime.clone();
	endTime.add(Calendar.MINUTE, duration);

	GregorianCalendar startgc = (GregorianCalendar) GregorianCalendar.getInstance();
	Date startDate = startTime.getTime();
	startgc.setTime(startDate);
	GregorianCalendar endgc = (GregorianCalendar) GregorianCalendar.getInstance();
	Date endDate = endTime.getTime();
	endgc.setTime(endDate);

	DatatypeFactory df = null;
	try {
	     df = DatatypeFactory.newInstance();
	} catch (javax.xml.datatype.DatatypeConfigurationException e) {
	    e.printStackTrace();
	    return false;
	}
	XMLGregorianCalendar startxgc = df.newXMLGregorianCalendar(startgc);
	XMLGregorianCalendar endxgc = df.newXMLGregorianCalendar(endgc);
;
	ReservationParams p = new ReservationParams();
	p.setUserName("fenius-user");
	p.setUserGroup("fenius-group");
	p.setReservationId("TEST");

	EndpointPair ep = new EndpointPair();
	ep.setA(apoint);
	ep.setZ(zpoint);
	p.setEndpoints(ep);

	/* if you want to set ExplicitRoute, use ep.add(ExplicitRoute). */
	{
	    List<ExplicitRoute> er = p.getExplicitRoute();
	}

	/* if you want to set EndpointParams, use ep.add(EndpointParams). */
	{
	    List<EndpointParams> epl = p.getEndpointParamsList();
	}

	/* set CommonParams(bandwidth, status, TimeParams) */
	{
	    CommonParams cp = new CommonParams();
	    cp.setBandwidth(new Integer(bw));
	    cp.setMaxDelay(new Integer(10));
	    cp.setMaxJitter(new Integer(10));
	    cp.setDirection(Direction.BI);
	    cp.setReliability(Reliability.NO);
	    cp.setStatus(Status.REQUESTING);

	    TimeParams tp = new TimeParams();
	    // tp.setScheduledStartTime(startxgc);
	    // tp.setScheduledEndTime(endxgc);
	    tp.setRequestedStartTime(startxgc);
	    tp.setRequestedEndTime(endxgc);
	    cp.setTimeParams(tp);
	    
	    AvpSeq avpseq = new AvpSeq();
	    List<Avp> avps = avpseq.getAvp();
	    Avp avp = new Avp();
	    avp.setAttrName("eth.vlan");
	    avp.setAttrVal(vlanid);
	    avps.add(avp);
	    cp.setAdhocParams(avpseq);

	    p.setEndtoendParams(cp);
	}

	/* if you want to set EndpointParams, use dp.add(EndpointParams). */
	{
	    List<DomainParams> dp = p.getDomainParams();
	}

	return create(p);
    }

    public boolean listTest() {
	return list();
    }
    
    public boolean cancelTest(String id) {
	return cancel(id);
    }
    
    public static void main(String[] args) {
	// String timezone = "Asia/Tokyo";
	TimeZone.setDefault(TimeZone.getTimeZone(timezone));

	try {
	    DraftFeniusService port;
	    if (isUseSSL) {
		port = initSoap(surl, qname);
	    } else {
		port = initSoap(purl, qname);
	    }

	    // URL url = new URL(surl);
	    // Draft service = new Draft(url, qname);
	    // DraftFeniusService port = service.getDraftFeniusServicePort();
	    DraftTest t = new DraftTest(port);

	    if (false) {
		// t = new DraftTest(port);
		int i = 15;
		while (i > 0) {
		    long T_create_s = System.nanoTime();
		    t.createTest();
		    long T_create_e = System.nanoTime();
		    // System.err.println("\t" + (T_create_e - T_create_s)/1000);
		    i--;

		    // System.gc();
		    Thread.sleep(1000);
		}
		System.exit(0);
	    }

	    if (args != null && args.length != 0 &&
		args[0].equals("create")) {
		t.createTest();
	    } else if (args != null && args.length != 0 &&
		       args[0].equals("list")) {
		t.listTest();
	    } else if (args != null && args.length != 0 &&
		       args[0].equals("cancel") && args[1] != null ) {
		t.cancelTest(args[1]);
	    } else {
		String s = "Test create | list | cancel id \n" +
		    "\t create:      create reservation\n" +
		    "\t list:        list reservation\n" +
		    "\t cancel id:   cancel reservation which has id \n";
		System.err.println(s);
	    }
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }
}